/**
 * @file
 * Worker thread implementation.
 *
 * Kisa provides spell checking as you type and displays the result in a small
 * window on your Desktop.
 *
 * Copyright (c) 2009 by Pete Black <theblackpeter@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * @author Pete Black <theblackpeter@gmail.com>
 */
#include "kisathread.h"

/**
 * Instances of this class will have this name.
 * @note this is not a unique identifier, just a common name.
 */
#ifndef OBJECT_NAME
#define OBJECT_NAME "kisathread"
#endif

/**
 * Name used when outputting debug information.
 */
#ifndef OBJECT_DEBUG_NAME
#define OBJECT_DEBUG_NAME "kisathread:"
#endif

KisaThread::KisaThread(QObject* parent) :
  QThread(parent) {
  isDone = false;

  // get the library instance
  kisaLib = KisaLib::instance();

  qDebug() << OBJECT_DEBUG_NAME << OBJECT_NAME << "created";
}

KisaThread::~KisaThread() {
  if(isRunning()) {
    qDebug() << OBJECT_DEBUG_NAME << "stopping thread";
    stop();
  }

  qDebug() << OBJECT_DEBUG_NAME << OBJECT_NAME << "destroyed";
}

void KisaThread::replaceInClient(const QString& misspelled,
                                 const QString& suggestion,
                                 const QString& residual) {
  // make sure that we have a valid event
  if(!lastKeyEvent.isNone()) {
    // disable any event subscriptions
    kisaLib->replaceInClient(lastKeyEvent.window(), misspelled, suggestion,
                             residual);
  }
}

void KisaThread::run() {
  qDebug() << OBJECT_DEBUG_NAME << "runnung event listening thread...";

  KisaEvent* kisaEvent;

  // this is the main event loop
  while(!isDone) {
    // block until a new event is available in the X even queue
    kisaEvent = kisaLib->nextEvent();

    // KeyPress events
    if(kisaEvent->type() == KisaEvent::KeyPress) {
      // make a copy for later use, there is no parent object so no worries
      // about it being deleted from elsewhere
      lastKeyEvent = *kisaEvent;

      // key may be a null character ('\0')
      const QChar key = kisaEvent->key();

      emit gotNewCharacter(key);
    }

    // MapNotify and CreateNotify events
    // if window was created or made "visible", we want notification...
    else if(kisaEvent->type() == KisaEvent::WindowActivate)
      kisaLib->addNotification(kisaEvent->window(), KisaEvent::WindowActivate);

    // VisibilityNotify events
    // change in the applications window state
    else if(kisaEvent->type() == KisaEvent::WindowStateChange)
      emit visibilityStateChange(kisaEvent->state());

    delete kisaEvent;
  }
}

void KisaThread::startToggle() {
  if(isRunning())
    stop();

  else {
    // this is the flag that stops the event loop above
    isDone = false;

    qDebug() << OBJECT_DEBUG_NAME << "subscribing for event notification from all visible windows...";

    // re-initialize all subscriptions to the X server
    kisaLib->reAddNotifications();

    qDebug() << OBJECT_DEBUG_NAME << "total number of windows:" << kisaLib->totalWindowSubscriptions();
    qDebug() << OBJECT_DEBUG_NAME << "starting thread";

    start();
  }
}

bool KisaThread::stop() {
  qDebug() << OBJECT_DEBUG_NAME << "stopping thread";

  if(!isRunning()) {
    qDebug() << OBJECT_DEBUG_NAME << "thread is not running, can't stop";
    return false;
  }

  // this is the flag that stops the event loop above
  isDone = true;

  // forcefully break any pending XNextEvent calls
  kisaLib->sendBogusXEvent();

  wait();

  // remove all subscriptions from the X server
  kisaLib->removeAllNotifications();

  qDebug() << OBJECT_DEBUG_NAME << "thread stopped";

  return true;
}
